import java.util.concurrent.atomic.AtomicStampedReference;


/**
 * 下面是一个详细的ABA问题的示例，使用了Java的`AtomicStampedReference`来演示这个问题。
 * 我将添加详细的注释来解释每一步的操作和ABA问题是如何发生的。
 *
 * ### 详细注释解释：
 *
 * 1. **BankAccount类**：这个类模拟了一个银行账户，使用`AtomicStampedReference`来保存账户的余额和版本号。
 *
 * 2. **deposit方法**：这个方法用于向账户存款。它接受存款金额和版本号数组作为参数。
 * 方法内部使用一个循环来不断尝试更新余额和版本号，直到成功为止。
 *
 * 3. **withdraw方法**：这个方法用于从账户取款。它同样接受取款金额和版本号数组作为参数。
 * 方法内部检查余额是否足够，然后尝试更新余额和版本号。
 *
 * 4. **main方法**：这是程序的入口点。我们创建了一个`BankAccount`对象，并初始化了一个版本号数组`stamp`。
 * 然后，我们创建了三个线程来模拟取款、存款和再次存款的操作。
 *
 * 5. **线程1**：模拟取款操作，从账户中取出50元。
 *
 * 6. **线程2**：模拟存款操作，向账户中存入100元。
 *
 * 7. **线程3**：再次模拟存款操作，实际上是将之前的存款撤销，存入-50元。
 *
 * 8. **join方法**：在主线程中，我们调用`join`方法来等待所有子线程完成。
 *
 * 9. **最终余额打印**：在所有操作完成后，我们打印出账户的最终余额。
 *
 * ### ABA问题解释：
 *
 * 在这个示例中，`AtomicStampedReference`用于保存账户余额和版本号。ABA问题发生在以下情况下：
 *
 * - 线程1读取余额为100，版本号为0，然后取出50元。
 * - 线程2读取余额为100，版本号为0，然后存入100元，余额变为200，版本号变为1。
 * - 线程3读取余额为200，版本号为1，然后存入-50元，余额变回150，版本号变为2。
 *
 * 此时，如果线程1再次检查余额，会发现余额仍然是100（因为线程3的操作实际上撤销了线程2的操作），
 * 但版本号已经从0变为2。这就是ABA问题，即账户余额的值虽然回到了初始值，但实际状态已经发生了变化（被取出过）。
 * 这个问题可以通过使用版本号来检测和解决。
 */
// 模拟银行账户类
class BankAccount {
    // 使用AtomicStampedReference来保存账户余额和版本号
     AtomicStampedReference<Integer> balance = new AtomicStampedReference<>(100, 0);

    // 存款方法
    public void deposit(int amount, int[] stamp) {
        while (true) {
            // 获取当前余额和版本号
            Integer currentBalance = balance.get(stamp);
            // 计算新的余额
            int newBalance = currentBalance + amount;
            // 尝试更新余额和版本号
            if (balance.compareAndSet(currentBalance, newBalance, stamp[0], stamp[0] + 1)) {
                System.out.println("存款成功，新的余额为: " + newBalance);
                break;
            }
        }
    }

    // 取款方法
    public void withdraw(int amount, int[] stamp) {
        while (true) {
            // 获取当前余额和版本号
            Integer currentBalance = balance.get(stamp);
            // 检查余额是否足够
            if (currentBalance < amount) {
                System.out.println("余额不足，无法取款");
                return;
            }
            // 计算新的余额
            int newBalance = currentBalance - amount;
            // 尝试更新余额和版本号
            if (balance.compareAndSet(currentBalance, newBalance, stamp[0], stamp[0] + 1)) {
                System.out.println("取款成功，新的余额为: " + newBalance);
                break;
            }
        }
    }
}

public class ABAProblemDemo {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        int[] stamp = new int[1]; // 用于保存版本号的数组

        // 线程1模拟取款操作
        Thread thread1 = new Thread(() -> {
            account.withdraw(50, stamp);
        });

        // 线程2模拟存款操作
        Thread thread2 = new Thread(() -> {
            account.deposit(100, stamp);
        });

        // 线程3再次模拟存款操作
        Thread thread3 = new Thread(() -> {
            account.deposit(-50, stamp); // 实际上是将之前的存款撤销
        });

        thread1.start();
        thread2.start();
        thread3.start();

        try {
            // 等待所有线程完成
            thread1.join();
            thread2.join();
            thread3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印最终余额
        System.out.println("最终余额: " + account.balance.get(stamp));
    }
}